/*
 * @Author: yangni 920556271@qq.com
 * @Date: 2024-01-09 18:02:45
 * @LastEditors: yangni 920556271@qq.com
 * @LastEditTime: 2024-05-16 11:44:16
 * @FilePath: /yt-devops/lib/pages/devices/device_controll/device_service_render/device_service_render_view.dart
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import 'dart:async';

import 'package:bruno/bruno.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:progress_loading_button/progress_loading_button.dart';
import 'package:yt_devops/components/device/device_button_group.dart';
import 'package:yt_devops/components/device/device_picker.dart';
import 'package:yt_devops/components/device/device_switch.dart';
import 'package:yt_devops/components/gaps.dart';
import 'package:yt_devops/configs.dart';
import 'package:yt_devops/model/data_type_config.dart';
import 'package:yt_devops/model/device_obj/product_config_model.dart';
import 'package:yt_devops/model/device_obj/prompt_rule_model.dart';
import 'package:yt_devops/model/device_obj/properties_model.dart';
import 'package:yt_devops/model/device_obj/property_group_model.dart';
import 'package:yt_devops/model/device_obj/service_config_model.dart';
import 'package:yt_devops/model/device_obj/spec/arrays_spec.dart';
import 'package:yt_devops/pages/devices/device_controll/device_controll_utils.dart';
import 'package:yt_devops/pages/devices/device_tab/utils/device_util.dart';
import 'package:yt_devops/resources/str_res_keys.dart';
import 'package:yt_devops/routers/router_path.dart';
import 'package:yt_devops/services/device/device_sevices.dart';
import 'package:yt_devops/styles/styles.dart';
import 'package:yt_devops/user_entity_manager.dart';
import 'package:yt_devops/utils/enums.dart';
import 'package:yt_devops/utils/func_utils.dart';

import '../device_property_group_render/device_property_group_render.dart';
import '../device_set_widget/device_set_widget.dart';

class DeviceServiceRenderWidget extends StatefulWidget {
  final int deviceId;
  final ServiceConfigModel serviceConfigModel;
  final ConnectStatusType? connectStatus;
  final Map<String, dynamic> dataSource;
  final Function(ServiceConfigModel)? setHandler;

  const DeviceServiceRenderWidget(
      {Key? key,
      required this.deviceId,
      required this.serviceConfigModel,
      required this.dataSource,
      this.connectStatus,
      this.setHandler})
      : super(key: key);

  @override
  State<DeviceServiceRenderWidget> createState() =>
      _DeviceServiceRenderWidgetState();
}

class _DeviceServiceRenderWidgetState extends State<DeviceServiceRenderWidget> {
  DeviceServices deviceServices = DeviceServices();
  bool editAuthority = true;

  /// 设备是否在线
  bool get isConnected => widget.connectStatus == ConnectStatusType.on;

  /// value 字体颜色
  Color get contentTextColor =>
      isConnected ? Colour.mainTextColor : Colour.offlineTextColor;

  @override
  void initState() {
    editAuthority = hasEditAuthority(widget.serviceConfigModel.authority);
    super.initState();
  }

  @override
  void didUpdateWidget(covariant DeviceServiceRenderWidget oldWidget) {
    if (oldWidget.serviceConfigModel.id != widget.serviceConfigModel.id) {
      editAuthority = hasEditAuthority(widget.serviceConfigModel.authority);
      setState(() {});
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    List propertyList = widget.serviceConfigModel.children ?? [];
    return SizedBox(
      width: MediaQuery.of(context).size.width,
      child: Column(
        children: [
          Offstage(
            offstage: isShowServiceName(widget.serviceConfigModel),
            child: DeviceControllUtils.createHead(
                widget.serviceConfigModel.name ?? '',
                tip: widget.serviceConfigModel.tip,
                isShowSetButton: !isRefresh(widget.serviceConfigModel.buttons),
                isShowRefreshButton:
                    isRefresh(widget.serviceConfigModel.buttons),
                buttonDisable: !canEdit, taphandler: () {
              Get.toNamed(
                RouterPath.deviceServiceSet,
                arguments: {
                  'deviceId': widget.deviceId,
                  'serviceConfigModel': widget.serviceConfigModel,
                  'dataSource': widget.dataSource
                },
              );
            }, refreshHandler: refreshServiceData),
          ),
          SizedBox(
            height: isHorizontalList(widget.serviceConfigModel.showType)
                ? 96
                : null,
            child: ListView.separated(
                shrinkWrap: true,
                scrollDirection:
                    isHorizontalList(widget.serviceConfigModel.showType)
                        ? Axis.horizontal
                        : Axis.vertical,
                physics: const NeverScrollableScrollPhysics(),
                itemBuilder: (context, index) {
                  var propertyMap = propertyList[index];

                  Widget comp;

                  if (propertyMap['type'] ==
                      DeviceConfigType.propertyGroup.name) {
                    comp = SizedBox(
                        width: 200,
                        child: DevicePropertyGroupRender(
                          propertyGroupModel:
                              PropertyGroupModel.fromJson(propertyMap),
                          dataSource: widget.dataSource,
                          connectStatus: widget.connectStatus,
                        ));
                  } else {
                    PropertiesModel property =
                        PropertiesModel.fromJson(propertyMap);

                    if (property.showType != null &&
                        property.showType != DeviceModelShowType.others) {
                      comp = widgetByShowType(
                        context,
                        widget.serviceConfigModel,
                        property,
                      );
                    } else {
                      comp = propertiesWidget(
                        property,
                        widget.dataSource[
                            DeviceControllUtils.getPropertyId(property)],
                      );
                    }
                  }

                  return Container(
                    padding: EdgeInsets.symmetric(horizontal: 16.w),
                    decoration: const BoxDecoration(
                      color: Colour.white,
                    ),
                    child: comp,
                  );
                },
                separatorBuilder: (context, index) => Gaps.line,
                itemCount: propertyList.length),
          )
        ],
      ),
    );
  }

  // 是否有刷新功能
  bool isRefresh(List? buttons) =>
      !FuncUtils.isListEmpty(buttons) && buttons!.contains('refresh');

  // 是否有参数设置功能
  bool isSet(List? buttons) =>
      !FuncUtils.isListEmpty(buttons) && buttons!.contains('edit');

  bool isHorizontalList(DeviceModelShowType? showType) =>
      showType == DeviceModelShowType.grid;

  Widget propertiesWidget(PropertiesModel property, dynamic value,
      {MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceBetween,
      int? dataIndex}) {
    Widget comp;

    switch (property.dataType?.type) {
      case DataTypeConfigEnum.double:
      case DataTypeConfigEnum.int:
      case DataTypeConfigEnum.string:
      case DataTypeConfigEnum.long:
      case DataTypeConfigEnum.enumType:
      case DataTypeConfigEnum.timestamp:
      case DataTypeConfigEnum.timeRange:
      case null:
        var name = dataIndex == null
            ? property.name ?? ''
            : arrayItemPropertyName(property, dataIndex);

        comp = textWidget(name, value, property,
            mainAxisAlignment: mainAxisAlignment);

        break;

      case DataTypeConfigEnum.struct:
        comp = structWidget(
            property.dataType?.specs as List<PropertiesModel>?, value);
        break;
      case DataTypeConfigEnum.array:
        comp = arrayWidget(property.dataType?.specs as ArraysSpec?, value);

        break;
      default:
        comp = Gaps.empty;
    }
    return comp;
  }

  String arrayItemPropertyName(PropertiesModel property, int index) {
    if (property.name?.contains('\${index}') == true) {
      return property.name!.replaceAll('\${index}', index.toString());
    } else {
      return '${property.name ?? ''}$index';
    }
  }

  Widget widgetByShowType(
    BuildContext context,
    ServiceConfigModel service,
    PropertiesModel model,
  ) {
    bool disabled = isDisabled(model);
    String? tip =
        DeviceControllUtils.isDisabledByExpression(model, widget.dataSource)
            ? model.tip
            : null;
    switch (model.showType) {
      case DeviceModelShowType.picker:
        var value = FuncUtils.formatModelValue(
            widget.dataSource[DeviceControllUtils.getPropertyId(model)] ??
                model.defaultValue,
            model.dataType);
        var spec = (model.dataType?.specs as Map?) ?? {};
        List<PickModel> list =
            spec.keys.map((e) => PickModel(name: spec[e], value: e)).toList();
        var prompt = FuncUtils.isListEmpty(service.promptRule)
            ? null
            : service.promptRule![0];
        return DevicePicker(
          model: model,
          ruleModel: prompt,
          value: value,
          list: list,
          disable: disabled,
          tip: tip,
          onChanged: (PickModel pickModel) async {
            var oldVal = widget.dataSource[model.id], newVal = pickModel.value;
            setState(() {});
            await asyncDialog(
                context: context,
                service: service,
                model: model,
                oldValue: oldVal,
                newValue: newVal);
            widget.dataSource.addAll({model.id: newVal});
          },
        );

      case DeviceModelShowType.switcher:
        var spec = (model.dataType?.specs as Map?) ?? {};
        bool value =
            widget.dataSource[DeviceControllUtils.getPropertyId(model)] ==
                    spec.keys.first
                ? true
                : false;

        return DeviceSwitch(
            tip: tip,
            value: value,
            model: model,
            disable: disabled,
            onChanged: (p0, p1) async {
              widget.dataSource.addAll({model.id: spec.keys.first});
              setState(() {});

              await asyncDialog(
                  context: context,
                  service: service,
                  model: model,
                  oldValue: null,
                  newValue: spec.keys.first);
              widget.dataSource.addAll({model.id: null});
            });

      case DeviceModelShowType.switchButton:
      case DeviceModelShowType.button:
        var value = FuncUtils.formatModelValue(
            widget.dataSource[DeviceControllUtils.getPropertyId(model)] ??
                model.defaultValue,
            model.dataType);
        var isButton = model.showType == DeviceModelShowType.button;
        var backgroudColor = disabled || !isButton
            ? Colour.backgroundColor
            : Colour.primaryColor;
        var textColor = disabled
            ? Colour.diabledButtoBgColor
            : isButton
                ? Colour.white
                : Colour.mainTextColor;

        return DeviceButtonGroup(
          backgroudColor: backgroudColor,
          textColor: textColor,
          refreshColor: isButton ? Colour.white : Colour.primaryColor,
          model: model,
          tip: tip,
          disable: disabled,
          onChanged: (newkey, newVal) async {
            await asyncDialog(
              context: context,
              service: service,
              model: model,
              newValue: newkey,
              oldValue:
                  widget.dataSource[DeviceControllUtils.getPropertyId(model)],
            );
          },
        );

      default:
        return Gaps.empty;
    }
  }

  Container textWidget(
    String name,
    dynamic value,
    PropertiesModel model, {
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceBetween,
  }) {
    var formateValue =
        FuncUtils.formatModelValue(value ?? model.defaultValue, model.dataType)
            .toString();
    bool canSet = isSet(model.buttons);
    var title = mainAxisAlignment == MainAxisAlignment.start ? '$name:' : name;
    return Container(
      color: Colour.white,
      height: 50,
      child: Row(
        mainAxisAlignment: mainAxisAlignment,
        children: [
          Expanded(
            child: Text(
              title,
              style: TextStyle(fontSize: 14.sp, color: Colour.otherTextColor),
            ),
          ),
          SizedBox(
            width: 8.w,
          ),
          GestureDetector(
            onTap: () {
              if (isConnected && canSet) {
                toSetProperty(model);
              }
            },
            child: Row(
              children: [
                Text(
                  formateValue,
                  style: TextStyle(fontSize: 14.sp, color: contentTextColor),
                ),
                Gaps.hGap10,
                // Text(
                //   unit,
                //   style: TextStyle(
                //       fontSize: 14.sp,
                //       color: contentTextColor,
                //       fontWeight: FontWeight.w500),
                // ),
                Offstage(
                  offstage: !canSet,
                  child: const Icon(Icons.chevron_right,
                      color: Colour.hintTextColor),
                )
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget arrayWidget(ArraysSpec? arraySpec, dynamic value) {
    if (arraySpec == null) {
      return Gaps.empty;
    }
    var arrayData = value is List ? value : FuncUtils.strToArray(value);
    if (arrayData.isEmpty) {
      /// 没有数据，伪造1条假数据
      var item = DeviceControllUtils.createNewDataForList(DataTypeConfig(
          type: DataTypeConfigEnum.array, spec: <String, dynamic>{})
        ..specs = arraySpec);
      arrayData.add(item);
    }

    DataTypeConfig? itemDataType = arraySpec.item;
    return ListView.separated(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemBuilder: (context, index) {
        Widget widget;
        switch (itemDataType?.type) {
          case null:
          case DataTypeConfigEnum.struct:
            widget = structWidget(
                itemDataType?.specs as List<PropertiesModel>?, arrayData[index],
                dataIndex: index + 1);
            break;
          case DataTypeConfigEnum.array:
            widget = arrayWidget(
                itemDataType?.specs as ArraysSpec, arrayData[index]);
            break;
          default:
            widget = propertiesWidget(
                itemDataType?.specs as PropertiesModel, arrayData[index]);
        }
        return widget;
      },
      itemCount: arrayData.length,
      separatorBuilder: (context, index) => Gaps.line,
    );
  }

  Widget structWidget(List<PropertiesModel>? specList, dynamic value,
      {int? dataIndex}) {
    if (FuncUtils.isListEmpty(specList)) {
      return Gaps.empty;
    }

    var structData = value is Map ? value : FuncUtils.strToMap(value);

    List<Widget> columns = [];

    int i = 0;

    do {
      if (i == 0) {
        columns.add(propertiesWidget(specList![i], structData[specList[i].id],
            dataIndex: dataIndex));
        i++;
      } else if (i + 1 >= specList!.length) {
        columns.add(propertiesWidget(
          specList[i],
          structData[specList[i].id],
        ));
        i++;
      } else {
        columns.add(
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Expanded(
                child: propertiesWidget(specList[i], structData[specList[i].id],
                    mainAxisAlignment: MainAxisAlignment.start),
              ),
              Expanded(
                child: propertiesWidget(
                    specList[i + 1], structData[specList[i + 1].id],
                    mainAxisAlignment: MainAxisAlignment.start),
              )
            ],
          ),
        );
        i = i + 2;
      }
    } while (i < specList.length);

    return Column(
      children: columns,
    );
  }

  /// 是否可以下发参数
  bool get canEdit =>
      editAuthority && widget.connectStatus == ConnectStatusType.on;

  /// 判断是否有编辑权限
  bool hasEditAuthority(List? authority) {
    return FuncUtils.isPassAuthority(authority?.map((e) => e.edit).toList(),
        permissionSource: UserEntityManager().webPermission);
  }

  bool isShowServiceName(ServiceConfigModel serviceConfigModel) {
    if (serviceConfigModel.showType == DeviceModelShowType.hideName) {
      return true;
    } else {
      return false;
    }
  }

  bool isDisabled(
    PropertiesModel? model,
  ) {
    return (widget.connectStatus != ConnectStatusType.on) ||
        DeviceControllUtils.isDisabledByExpression(model, widget.dataSource) ||
        !hasEditAuthority(
          model?.authority,
        );
  }

  // 去设置某个属性
  toSetProperty(PropertiesModel model) {
    final GlobalKey<DeviceSetWidgetState> childKey = GlobalKey();
    BrnDialogManager.showConfirmDialog(context,
        title: model.name ?? '',
        messageWidget: Padding(
          padding: const EdgeInsets.only(
            top: 12,
          ),
          child: DeviceSetWidget(
            key: childKey,
            deviceId: widget.deviceId,
            data: widget.dataSource,
            serviceConfigModel:
                widget.serviceConfigModel.copyWith(childrenCope: [model]),
          ),
        ),
        cancel: ResKey.cancel.tr,
        confirm: ResKey.confirm.tr,
        onCancel: () => Get.back(),
        onConfirm: () {
          {
            if (DeviceControllUtils.validate(
                [model], childKey.currentState!.dataSource)) {
              Get.back();
              childKey.currentState!.sendCommand(
                currentContext: context,
              );
            }
          }
        });
  }

  Future toSendCommand({
    required ServiceConfigModel service,
    required PropertiesModel property,
    value,
    bool showLoading = true,
  }) async {
    await DeviceControllUtils.remoteSetting(
        context: context,
        service: service,
        property: property,
        data: {property.id: value},
        deviceId: widget.deviceId,
        showLoading: showLoading);
  }

  Future asyncDialog({
    required BuildContext context,
    required ServiceConfigModel service,
    required PropertiesModel model,
    required dynamic oldValue,
    required dynamic newValue,
  }) {
    Completer completer = Completer();
    List<PromptRuleModel>? ruleList =
        DeviceControllUtils.getPromptRuleList(service, model);
    if (ruleList == null) {
      toSendCommand(
              service: service,
              property: model,
              value: newValue,
              showLoading: false)
          .then((value) {
        completer.complete();
      });
    } else {
      DeviceControllUtils.showConfirmDialog(
          context, ruleList, model, oldValue, newValue, onConfirm: () async {
        if (newValue != null) {
          await toSendCommand(
              service: service,
              property: model,
              value: newValue,
              showLoading: false);
        }
        completer.complete();
      }, onCancel: () {
        completer.complete();
      });
    }
    return completer.future;
  }

  /// 更新数据
  Future refreshServiceData() async =>
      await Configs.httpClient.requestWithLoading(() async {
        var modelIdList =
            widget.serviceConfigModel.children?.map((e) => e['id']).toList() ??
                [];
        await deviceServices.refreshRemoteSetting({
          'deviceId': widget.deviceId,
          'serviceId': widget.serviceConfigModel.queryId ?? 'queryParam',
          'input': {
            'queryList': modelIdList,
          },
        });
      }, showLoading: false);
}
